﻿using System;
using System.Diagnostics;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Threading;
using System.Reflection;
using INI;

using FullMotion.LiveForSpeed.InSim;
using FullMotion.LiveForSpeed.InSim.Enums;
using FullMotion.LiveForSpeed.InSim.Events;

namespace StormCup_Qual
{
    class Program
    {
        private string ip;
        private int port;
        private string adminPass;
        private char prefix;
        private byte currentPlayerID;
        private byte nextPlayerID;
        private int positionREO;
        private int REOlength;
        private int groupnumber;
        private int groupsleep;

        private string ProgramTitle;
        private string ProgramVersion;
        private string ProgramCompany;

        private InSimHandler handler = new InSimHandler();
        private Hashtable racerDatabase = new Hashtable();
        private Hashtable REODatabase = new Hashtable();

        private LfsButton table = new LfsButton(0);

        private Queue Qqueue = new Queue();
        private int QualificationLaps = 1;

        static void Main(string[] args)
        {
            Program p = new Program();
            p.Config();
            p.Setup();
            p.Start();
            p.Threading();
        }

        public void Config()
        {
            IniFile ini = new IniFile(".\\config.ini");
            ip = ini.IniReadValue("server", "ip");
            port = Convert.ToInt32(ini.IniReadValue("server", "port"));
            adminPass = ini.IniReadValue("server", "admin pass");
            prefix = Convert.ToChar(ini.IniReadValue("server", "insim prefix"));
            groupnumber = Convert.ToInt16(ini.IniReadValue("server", "number"));
            //groupnumber++;
            groupsleep = Convert.ToInt16(ini.IniReadValue("server", "sleep"));
            if (groupsleep != 0)
                groupsleep = groupsleep * 1000;
        }

        public void Setup()
        {
            table.Color = ButtonColor.Light;
            table.Height = 10;
            table.Left = 10;
            table.Text = "";
            table.TextAlignment = ButtonTextAlignment.Center;
            table.TextColor = ButtonTextColor.SelectedText;
            table.Top = 180;
            table.Width = 180;

            handler.Configuration.LFSHost = ip;
            handler.Configuration.LFSHostPort = port;
            handler.Configuration.AdminPass = adminPass;
            handler.Configuration.UseTCP = true;
            handler.Configuration.ProgramName = "Test";
            handler.Configuration.Prefix = prefix;

            handler.LFSMessage += new FullMotion.LiveForSpeed.InSim.EventHandlers.MessageEventHandler(handler_LFSMessage);
            handler.RaceTrackConnection += new FullMotion.LiveForSpeed.InSim.EventHandlers.RaceTrackNewConnectionHandler(handler_RaceTrackConnection);
            handler.RaceTrackConnectionLeave += new FullMotion.LiveForSpeed.InSim.EventHandlers.RaceTrackConnectionLeaveHandler(handler_RaceTrackConnectionLeave);
            handler.RaceTrackPlayer += new FullMotion.LiveForSpeed.InSim.EventHandlers.RaceTrackPlayerHandler(handler_RaceTrackPlayer);
            handler.RaceTrackPlayerLap += new FullMotion.LiveForSpeed.InSim.EventHandlers.RaceTrackPlayerLapHandler(handler_RaceTrackPlayerLap);
            handler.RaceTrackPlayerLeave += new FullMotion.LiveForSpeed.InSim.EventHandlers.RaceTrackPlayerLeaveHandler(handler_RaceTrackPlayerLeave);
            handler.RaceTrackPlayerPits += new FullMotion.LiveForSpeed.InSim.EventHandlers.RaceTrackPlayerPitsHandler(handler_RaceTrackPlayerPits);
            handler.RaceTrackReorder += new FullMotion.LiveForSpeed.InSim.EventHandlers.RaceTrackReorderHandler(handler_RaceTrackReorder);
            handler.RaceTrackRaceStart += new FullMotion.LiveForSpeed.InSim.EventHandlers.RaceTrackRaceStartHandler(handler_RaceTrackRaceStart);

            handler.RaceTrackPlayerPitlane += new FullMotion.LiveForSpeed.InSim.EventHandlers.RaceTrackPlayerPitlaneHandler(handler_RaceTrackPlayerPitlane);
        }

        void handler_RaceTrackPlayerPitlane(InSimHandler sender, RaceTrackPlayerPitlane e)
        {
            lfsracer racer = null;
            switch (e.EventType)
            {
                case Pitlane.DriveThrough:
                    break;
                case Pitlane.Enter:
                    racer = (lfsracer)racerDatabase[getConnectionid(e.PlayerId)];
                    if (racer != null)
                    {
                        if (racer.Status == lfsracer.RacerQStatus.inlap)
                        {
                            racer.Status = lfsracer.RacerQStatus.finished;
                            //handler.SendMessage(racer.username + " thanks, please drive into the pitbox and spectate");
                            handler.SendMessageToConnection("Thanks, please drive into a pitbox and spectate", racer.connectionid);
                        }

                    }
                    break;
                case Pitlane.Exit:
                    racer = (lfsracer)racerDatabase[getConnectionid(e.PlayerId)];
                    if (racer != null)
                    {
                        if (Qqueue.Count > 0)
                        {
                            lfsracer Qualifier = (lfsracer)Qqueue.Peek();
                            if (Qualifier != null)
                            {
                                if (racer.username.Equals(Qualifier.username))
                                {
                                    // We got the first one in the Queue :)
                                    racer.Status = lfsracer.RacerQStatus.outlap;
                                    //handler.SendMessage("Racer Left the pitlane, Next one lineup :) ");
                                    this.AdminCall(racer.playername + " left the pitlane.");
                                    Qqueue.Dequeue(); // It returns a racer class, but we don't need any info from it :)

                                    if (Qqueue.Count == 0)
                                    {
                                        //handler.SendMessage(racer.username + " is the last one");
                                        this.AdminCall(racer.playername + " is the last one");
                                    }

                                    // Check again, if the queue is empty!
                                    if (Qqueue.Count > 0)
                                    {
                                        lfsracer nextracer = (lfsracer)Qqueue.Peek();
                                        if (nextracer != null)
                                        {
                                            //handler.SendMessage(nextracer.username + " your next!");
                                            handler.SendMessageToConnection("You are next!", nextracer.connectionid);
                                            this.AdminCall(nextracer.username + " is next!");
                                        }
                                    }
                                }
                            }
                            else
                            {
                                Qqueue.Dequeue();
                                if (Qqueue.Count > 0)
                                {
                                    lfsracer NextQualifier = (lfsracer)Qqueue.Peek();
                                    if (NextQualifier != null)
                                    {
                                        if (racer.username.Equals(NextQualifier.username))
                                        {
                                            // We got the first one in the Queue :)
                                            racer.Status = lfsracer.RacerQStatus.outlap;
                                            //handler.SendMessage("Racer Left the pitlane, Next one lineup :) ");
                                            this.AdminCall(racer.playername + " left the pitlane.");
                                            Qqueue.Dequeue(); // It returns a racer class, but we don't need any info from it :)

                                            if (Qqueue.Count == 0)
                                            {
                                                //handler.SendMessage(racer.username + " is the last one");
                                                this.AdminCall(racer.playername + " is the last one");
                                            }

                                            // Check again, if the queue is empty!
                                            if (Qqueue.Count > 0)
                                            {
                                                lfsracer nextracer = (lfsracer)Qqueue.Peek();
                                                if (nextracer != null)
                                                {
                                                    //handler.SendMessage(nextracer.username + " your next!");
                                                    handler.SendMessageToConnection("You are next!", nextracer.connectionid);
                                                    this.AdminCall(nextracer.username + " is next!");
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }

                    break;
                case Pitlane.NoPurpose:
                    break;
                case Pitlane.Num:
                    break;
                case Pitlane.StopGo:
                    break;
                default:
                    break;
            }
            

        }

        void handler_LFSMessage(InSimHandler sender, Message e)
        {
            if (racerDatabase.ContainsKey(e.ConnectionId))
            {
                lfsracer racer = (lfsracer)racerDatabase[e.ConnectionId];

                if (e.UserType == UserType.Prefix)
                {
                    if (e.MessageText.Contains("qlist".ToLower()))
                    {
                        if (Qqueue.Count > 0)
                        {
                            if (Qqueue.Count == 1)
                                handler.SendMessageToConnection(Qqueue.Count + " qualifier", e.ConnectionId);
                            else
                                handler.SendMessageToConnection(Qqueue.Count + " qualifiers", e.ConnectionId);
                            handler.SendMessageToConnection("Queue from top to bottom:", e.ConnectionId);
                            int position = 1;
                            lock (Qqueue)
                            {
                                foreach (lfsracer lfsracer in Qqueue)
                                {
                                    handler.SendMessageToConnection(position++ + ". " + lfsracer.playername, e.ConnectionId);
                                }
                            }
                        }
                        else
                        {
                            handler.SendMessageToConnection("Q List is empty, 0 qualifiers", e.ConnectionId);
                        }
                    }

                    // An Admin command! to add a late racer to the bottom of the list :P
                    if (e.MessageText.Contains("qadd".ToLower()))
                    {
                        string username = e.MessageText;
                        username = e.MessageText.Substring(username.IndexOf("qadd ") + 5);

                        lfsracer lateQracer = (lfsracer)racerDatabase[getConnectionid(username)];
                        if (racer != null)
                        {
                            Qqueue.Enqueue(lateQracer);
                            AdminCall(lateQracer.playername + " added to the Qualification List");
                        }
                    }
                }
            }
        }

        void handler_RaceTrackConnection(InSimHandler sender, RaceTrackConnection e)
        {
            if (e.ConnectionId != 0)
            {
                if (!racerDatabase.ContainsKey(e.ConnectionId))
                {
                    lfsracer racer = new lfsracer(e.UserName, e.ConnectionId);
                    racer.playername = e.PlayerName;
                    racer.isadmin = e.IsAdmin;
                    racer.isremote = e.IsRemote;
                    racerDatabase.Add(e.ConnectionId, racer);
                }
            }
        }

        void handler_RaceTrackConnectionLeave(InSimHandler sender, RaceTrackConnectionLeave e)
        {
            if (racerDatabase.ContainsKey(e.ConnectionId))
            {
                lfsracer racer = (lfsracer)racerDatabase[e.ConnectionId];
                if (racer != null)
                {
                    if (Qqueue.Count > 0)
                    {
                        lfsracer nextQ = (lfsracer)Qqueue.Peek();
                        if (nextQ != null)
                        {
                            if (racer.username == nextQ.username)
                            {
                                Qqueue.Dequeue();// Remove the player
                                // and invite the next one!
                                if (Qqueue.Count > 0)
                                {
                                    lfsracer secondracer = (lfsracer)Qqueue.Peek();
                                    if (secondracer != null)
                                    {
                                        handler.SendMessageToConnection(secondracer.playername + " Your turn, Good luck", secondracer.connectionid);
                                        AdminCall(secondracer.playername + " is next");
                                    }
                                }
                            }

                        }
                    }
                }
                // Finally remove it
                racerDatabase.Remove(e.ConnectionId);
            }
        }

        void handler_RaceTrackPlayer(InSimHandler sender, RaceTrackPlayer e)
        {
            if (racerDatabase.ContainsKey(e.ConnectionId))
            {
                lfsracer racer = (lfsracer)racerDatabase[e.ConnectionId];
                racer.plate = e.Plate;
                racer.playerid = e.PlayerId;
                racer.isracing = true;

                if (Qqueue.Count > 0)
                {
                    lfsracer Qualifier = (lfsracer)Qqueue.Peek();
                    if (racer.username.Equals(Qualifier.username))
                    {
                        // We got the first one in the Queue :)
                        racer.Status = lfsracer.RacerQStatus.warminguplap;
                        //handler.SendMessage(racer.username + " status : " + racer.Status);
                        AdminCall(racer.playername + " status : " + racer.Status);
                    }
                    else
                    {
                        AdminCall(racer.playername + " tried to join,NoGo! " + Qualifier.playername + " needs his run first");
                        //handler.SendMessage(racer.username + " tried to join. No Go: " + Qualifier.username + " needs his run first");
                        handler.SendMessage("/spec " + racer.username);
                    }
                }
                else
                {
                    if (racer.Status == lfsracer.RacerQStatus.outlap)
                    {
                        handler.SendMessage(racer.username + " did his lap, no join now");
                        handler.SendMessage("/spec " + racer.username);
                    }
                }

            }
        }

        void handler_RaceTrackPlayerLap(InSimHandler sender, RaceTrackPlayerLap e)
        {

            lfsracer racer = (lfsracer)racerDatabase[getConnectionid(e.PlayerId)];

            if (racer != null)
            {
                switch (racer.Status)
                {
                    case lfsracer.RacerQStatus.warminguplap:
                        racer.Status = lfsracer.RacerQStatus.qualificationlap;
                        handler.SendMessageToConnection("Qualifing lap, Good Luck!",racer.connectionid);
                        //handler.SendMessage(racer.username + " is going todo his Qualy Lap now!");
                        AdminCall(racer.playername + " is going todo his Qualy Lap now!");
                        break;
                    case lfsracer.RacerQStatus.qualificationlap:
                        if ((e.LapsDone -1) == QualificationLaps)
                        {
                            racer.Status = lfsracer.RacerQStatus.outlap;
                            handler.SendMessageToConnection("drive safely to the pitlane and enter the pitbox", racer.connectionid);
                            //handler.SendMessage(racer.username + " finished his Qualy lap, outlap now!");
                            AdminCall(racer.playername + " finished his Qualy lap, outlap now!");
                        }
                        else
                        {
                            handler.SendMessage(Math.Abs(QualificationLaps - e.LapsDone).ToString() + " laps left");
                        }
                        break;
                    case lfsracer.RacerQStatus.outlap:
                        AdminCall("dude: " + racer.playername + " 1 lap too much!");
                        //handler.SendMessage("dude: " + racer.username + " 1 lap too much!");
                        handler.SendMessage("/spec " + racer.username);
                        break;
                    default:
                        break;
                }                
            }


            /*
            if ((positionREO > REOlength) | (REOlength == 0))
            {

            }
            else
            {
                if ((groupsleep != 0) && (groupnumber > 1))
                {
                    for (int i = 0; i < groupnumber; i++)
                    {
                        if (currentPlayerID == e.PlayerId)
                        {
                            //lfsracer currentracer = (lfsracer)racerDatabase[getConnectionid(currentPlayerID)];
                            lfsracer nextracer = (lfsracer)racerDatabase[getConnectionid(nextPlayerID)];

                            handler.SendMessage(nextracer.playername + TextColor.White + ", you are next");//, nextracer.connectionid);

                            currentPlayerID = nextPlayerID;
                            nextPlayerID = GetReoPlayerID(positionREO);
                            positionREO++;
                            if (positionREO > REOlength)
                                break;
                        }
                        Thread.Sleep(groupsleep);
                    }
                }
                else
                {
                    if (currentPlayerID == e.PlayerId)
                    {
                        //lfsracer currentracer = (lfsracer)racerDatabase[getConnectionid(currentPlayerID)];
                        lfsracer nextracer = (lfsracer)racerDatabase[getConnectionid(nextPlayerID)];

                        handler.SendMessage(nextracer.playername + TextColor.White + ", you are next");//, nextracer.connectionid);

                        currentPlayerID = nextPlayerID;
                        nextPlayerID = GetReoPlayerID(positionREO);
                        positionREO++;
                    }
                }

                //int j = positionREO + groupnumber;
                //string s = "";
                //for (int i = positionREO; i < j; i++)
                //{
                //    lfsracer r = (lfsracer)racerDatabase[GetReoPlayerID(i)];
                //    s += r.playername + ", ";
                //}
                //table.Text = s;
                //handler.ShowButton(table, true);
            }
             */

        }

        void handler_RaceTrackPlayerLeave(InSimHandler sender, RaceTrackPlayerLeave e)
        {
            if (racerDatabase.ContainsKey(getConnectionid(e.PlayerId)))
            {
                lfsracer racer = (lfsracer)racerDatabase[getConnectionid(e.PlayerId)];
                racer.isracing = false;                
            }
        }

        void handler_RaceTrackPlayerPits(InSimHandler sender, RaceTrackPlayerPits e)
        {
            if (racerDatabase.ContainsKey(getConnectionid(e.PlayerId)))
            {
                lfsracer racer = (lfsracer)racerDatabase[getConnectionid(e.PlayerId)];
                racer.isracing = false;
            }
        }

        void handler_RaceTrackReorder(InSimHandler sender, RaceTrackReorder e)
        {
            
            


            REODatabase.Clear();
            for (int i = 0; i < e.OrderedPlayerIds.Length; i++)
            {
                lfsracer racer = (lfsracer)racerDatabase[getConnectionid(e.OrderedPlayerIds[i])];
                Qqueue.Enqueue(racer);
                //REODatabase.Add(e.OrderedPlayerIds[i], i);
            }

            //handler.SendMessage("Total Queue: " + Qqueue.Count);            
            //handler.SendMessage("Everyone to Spectate :)");
            //handler.SendMessage("Queue List: ");
            foreach (lfsracer lfsracer in Qqueue)
            {
                //handler.SendMessage(lfsracer.username);
                handler.SendMessageToConnection("No worries, Everybody is spectated!", lfsracer.connectionid);
                handler.SendMessage("/spec " + lfsracer.username);
            }


            currentPlayerID = 0;
            nextPlayerID = e.OrderedPlayerIds[0];
            positionREO = 1;
            REOlength = e.OrderedPlayerIds.Length;
        }

        void handler_RaceTrackRaceStart(InSimHandler sender, RaceTrackRaceStart e)
        {
            handler.SendMessage("Qualification Starts! Good luck everyone!");
            handler.SendMessage("Use $qlist, to see the qualification order.");
            handler.SendMessage("You will be called todo your qualification run, don't join untill then!");
            if (Qqueue.Count > 0)
            {
                lfsracer Qualifier = (lfsracer)Qqueue.Peek();
                if (Qualifier != null)
                {
                    handler.SendMessageToConnection(Qualifier.playername + " Your turn, Good luck", Qualifier.connectionid);
                    AdminCall(Qualifier.playername + " is next");
                }                
            }

            /*

            if ((groupsleep != 0) && (groupnumber > 1))
            {
                for (int i = 0; i < groupnumber; i++)
                {
                    //lfsracer currentracer = (lfsracer)racerDatabase[getConnectionid(currentPlayerID)];
                    lfsracer nextracer = (lfsracer)racerDatabase[getConnectionid(nextPlayerID)];

                    handler.SendMessage(nextracer.playername + TextColor.White + ", you are next");//, nextracer.connectionid);

                    currentPlayerID = nextPlayerID;
                    nextPlayerID = GetReoPlayerID(positionREO);
                    positionREO++;
                    if (positionREO > REOlength)
                        break;

                    Thread.Sleep(groupsleep);
                }
            }
            else
            {
                //lfsracer currentracer = (lfsracer)racerDatabase[getConnectionid(currentPlayerID)];
                lfsracer nextracer = (lfsracer)racerDatabase[getConnectionid(nextPlayerID)];

                handler.SendMessage(nextracer.playername + TextColor.White + ", you are next");//, nextracer.connectionid);

                currentPlayerID = nextPlayerID;
                nextPlayerID = GetReoPlayerID(positionREO);
                positionREO++;
            }
            */

            //int j = positionREO + groupnumber;
            //string s = "";
            //for (int i = positionREO; i < j; i++)
            //{
            //    lfsracer r = (lfsracer)racerDatabase[GetReoPlayerID(i)];
            //    s += r.playername + ", ";
            //}
            //table.Text = s;
            //handler.ShowButton(table, true);
        }

        public void Start()
        {
            try
            {
                Assembly SampleAssembly = Assembly.Load(Assembly.GetExecutingAssembly().FullName);
                foreach (Attribute a in SampleAssembly.GetCustomAttributes(true))
                {
                    if (a is AssemblyTitleAttribute)
                    {
                        ProgramTitle = (a as AssemblyTitleAttribute).Title;
                    }
                    if (a is AssemblyFileVersionAttribute)
                    {
                        ProgramVersion = (a as AssemblyFileVersionAttribute).Version;
                    }
                    if (a is AssemblyCompanyAttribute)
                    {
                        ProgramCompany = (a as AssemblyCompanyAttribute).Company;
                    }
                }

                Console.Title = ProgramTitle + " " + ProgramVersion;
                Console.WriteLine(ProgramTitle);
                Console.WriteLine("Version: " + ProgramVersion);
                Console.WriteLine(ProgramCompany);
                Console.WriteLine("Connecting");
                handler.Initialize(10);
                Console.WriteLine("Connected");
                handler.SendMessage("Race Control System Restarting...");
                //handler.SendMessage(ProgramTitle);
                //handler.SendMessage("Version: " + ProgramVersion);
                //handler.SendMessage(ProgramCompany);
                handler.RequestConnectionInfo();
                handler.RequestPlayerInfo();
            }
            catch (Exception ex)
            {
                Console.WriteLine();
                Console.WriteLine(ex.Message);
                Console.WriteLine(ex.Source);
                Console.WriteLine(ProgramTitle + " could not connect. Please check your ip, port and admin pass settings.");
                Console.ReadLine();
                Environment.Exit(0);
            }
        }

        public void Threading()
        {
            while (handler.State == InSimHandler.HandlerState.Connected)
            {
                Thread.Sleep(1);
            }
        }

        public byte getConnectionid(byte playerid)
        {
            try
            {
                foreach (System.Collections.DictionaryEntry Entry in this.racerDatabase)
                {
                    lfsracer racer = (lfsracer)Entry.Value;
                    if (racer.playerid == playerid)
                        return racer.connectionid;
                }
                return (byte)0;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                return (byte)0;
            }
        }

        public byte getConnectionid(string username)
        {
            try
            {
                foreach (System.Collections.DictionaryEntry Entry in this.racerDatabase)
                {
                    lfsracer racer = (lfsracer)Entry.Value;
                    if (racer.username == username)
                        return racer.connectionid;
                }
                return (byte)0;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                return (byte)0;
            }
        }

        public byte GetReoPlayerID(int i) // get playerid from reodb from position i
        {
            try
            {
                foreach (DictionaryEntry Entry in REODatabase)
                {
                    int test = (int)Entry.Value;
                    if (test == i)
                        return (byte)Entry.Key;
                }
                return (byte)0;
            }
            catch (Exception ex)
            {
                return (byte)0;
            }
        }

        public void AdminCall(string message)
        {
            try
            {
                // To make it threadsafe, even when hashtable is synchronised, another thread can access it.
                // You can prevent this, with 'locking' the object. (not sure if an error can occur somewhere else. try/catch ect
                //lock (this.racerDatabase)
                {
                    foreach (System.Collections.DictionaryEntry Entry in this.racerDatabase)
                    {
                        lfsracer racer = (lfsracer)Entry.Value;
                        if (racer.isadmin)
                        {
                            // Send the message to each admin on the server.
                            handler.SendMessageToConnection(message, racer.connectionid);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }

        }

       
    }
}
